package xib.parserTools.adaptee;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import xib.parserTools.data.ChapterData;
import xib.parserTools.interfaces.IAdapterParser;
import xib.parserTools.interfaces.IChapterData;
import xib.parserTools.data.HeaderData;
import xib.parserTools.data.PageData;
import xib.parserTools.data.PanelData;
import xib.parserTools.data.SpeechBubbleData;
import xib.parserTools.interfaces.IPanelData.PanelType;
import xib.parserTools.interfaces.ISpeechBubbleData;
import xib.utils.XibException;


/**
 * Implementation from the adapter for the version 1 with a
 * JavaXML implementation
 * @author Xavier,Jose Miguel
 */
public class AdapteeParserDOM implements IAdapterParser{

    /**
     *  Document with the complet file
     */
    private Document document;

    /**
     *  Header information
     */
    private HeaderData header;

    /**
     * List with the chapters information
     */
    private ArrayList<IChapterData> chapters;
    private int actual_chapter_number;
    private int actual_page_number;

    /**
     * Default constructor
     */
    public AdapteeParserDOM()
    {
        header = new HeaderData();
    }


    /**
     * Function who's in charge to open the document
     * @param filename The name of the file to open
     * @throws IOException If the file dont exist
     */
    public void loadInputStream(InputStream input) throws IOException{
        try{

            //Initialize components for the JavaxXML
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder;
            builder = dbf.newDocumentBuilder();
            document = builder.parse(input);

            // Init all the data
            init();
        }
        catch(IOException ex){
            throw ex;
        }
        catch (SAXException ex) {
            throw new IOException("Error parsing the file: " + ex.getMessage());
        }catch (ParserConfigurationException ex) {
            throw new IOException("Parser Configuration Exception: " + ex.getMessage());
        }
    }

    /**
     * Validate a file with a XMLSchema
     * @param pathname The absolute pathname of the file to open
     * @return Return true if the file is incorrect
     * @throws IOException If the file dont exist
     * @throws SAXException If the file is incorrect
     */
    public boolean ValidateFile(String pathname) throws ParserConfigurationException, SAXException, IOException
    {
        try {
            //Initialize components for the JavaxXML
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setNamespaceAware(true);
            dbf.setValidating(true);
            dbf.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage",
                    "http://www.w3.org/2001/XMLSchema");
            DocumentBuilder builder;
            builder = dbf.newDocumentBuilder();
            builder.setErrorHandler(new SimpleErrorHandler());
            document = builder.parse(new FileInputStream(pathname));


        } catch (ParserConfigurationException ex) {
            throw ex;
        } catch (SAXException ex) {
            throw ex;
        } catch (IOException ex) {
            throw ex;
        }

        return true;
    }

    /**
     * Initialize all of the elements of the adaptee parsing the open file
     */
    private void init(){

        Element dom = document.getDocumentElement();
        Element head;

        //Obtain the header element
        head = (Element)  dom.getElementsByTagName("header").item(0);

        //Init the header
        initHeader(head);

        // Obtain the chapters
        NodeList listChapters = dom.getElementsByTagName("chapter");
        initChapters(listChapters);


    }

    /**
     * Init the header data
     * @param head Element head of the document
     */
    private void initHeader(Element head){

        //Obtain the tag languages
        Element languages;

        languages = (Element) head.getElementsByTagName("languages").item(0);

        //Init the header with the languages
        initHeaderLanguages(languages);

        //Obtain the titles
        NodeList titleNodes;

        //Get the list of nodes "title"
        titleNodes = head.getElementsByTagName("title");

        //Init the header with the titles
        initHeaderTitles(titleNodes);


        //Obtain the authors
        NodeList authorNodes;

        authorNodes = head.getElementsByTagName("author");

        try {
            initHeaderAuthors(authorNodes);
        } catch (XibException ex) {
            System.out.println("XIBException : " + ex.getMessage());
        }

        //Obtain the page size
        Element pageSize;
        pageSize = (Element)head.getElementsByTagName("page_size").item(0);

        //Init the default page size
        initHeaderPageSize(pageSize);


    }
    /**
     * Init the list of chapters
     * @param chters List of chapters node
     */
    private void initChapters(NodeList listChapters) {

        chapters = new ArrayList<IChapterData>();

        // For each chapter, init its elements
        for(int i = 0; i < listChapters.getLength(); i++)
        {
            initNewChapter( (Element)listChapters.item(i));
        }

    }
    /**
     * Init a new chapter and adding it into the chapter list
     * @param chapter The chapter who's going to be parsed
     */
    private void initNewChapter(Element chapter){

        ChapterData chapterData = new ChapterData();

        //Set titles of the chapter
        NodeList listTitles;
        String nameTitles;
        String langTitles;

        // Get the titles
        listTitles = chapter.getElementsByTagName("title");


        for(int i = 0; i < listTitles.getLength(); i++)
        {
            nameTitles = ((Element)listTitles.item(i)).getFirstChild().getNodeValue();
            langTitles = ((Element)listTitles.item(i)).getAttribute("language");

            chapterData.addTitle(nameTitles, langTitles);
        }


        //Get the number of the chapter
        // TODO: Put catch exception here
        int number;
        number = Integer.parseInt(chapter.getAttribute("number"));
        actual_chapter_number = number; 

        //Set into the chapter data
        chapterData.setChapterNumber(number);

        //Get the pages an add to the chapterData
        addPages(chapterData, chapter);
        //Set the new chapter into the list of chapters
        chapters.add(chapterData);

    }

    /***
     * Add the diferents pages to a chapterData
     * @param chapterData ChapterData to add the pages
     * @param chapter Chapter with the list of pages
     */
    private void addPages(ChapterData chapterData, Element chapter) {

        NodeList pages;
        Element pageAux;
        PageData pageData;

        pages = chapter.getElementsByTagName("page");

        for(int i = 0; i < pages.getLength(); i++)
        {
            actual_page_number = i + 1; 
            pageAux = (Element) pages.item(i);
            pageData = initPage(pageAux);
            chapterData.addPage(pageData);
        }
    }

    /**
     * Init a new page
     * @param page Element page
     * @return PageData with the elements of the page
     */
    public PageData initPage(Element page)
    {
        PageData pageData = new PageData();

        String height, width;

        height = page.getAttribute("height");
        width = page.getAttribute("width");

        // TODO: Put catch exception here
        if(!height.equals("") && !width.equals(""))
            pageData.setWidthAndHeight(Integer.parseInt(width), Integer.parseInt(height));
        else
            pageData.setWidthAndHeight( header.getDefaultPageWidth(),header.getDefaultPageHeight());

        NodeList panels = page.getElementsByTagName("panel");

        Element panel;
        for(int i = 0; i < panels.getLength(); i++)
        {

            panel = (Element) panels.item(i);
            pageData.addPanel( initPanel(panel));

        }

        return pageData;
    }

    /**
     * Init a new panel
     * @param panel Element panel to get the data
     * @return PanelData with the data of the panel
     */
    private PanelData initPanel( Element panel)
    {
        PanelData panelData = new PanelData();

        int height, width;
        int positionX, positionY;
        int order;
        String identifier;

        // TODO: Put catch expection here
        height = Integer.parseInt(panel.getAttribute("height"));
        width  = Integer.parseInt(panel.getAttribute("width"));

        panelData.setWidthAndHeight(width, height);

        positionX = Integer.parseInt(panel.getAttribute("positionX"));
        positionY = Integer.parseInt(panel.getAttribute("positionY"));

        panelData.setLocationXY(positionX, positionY);

        order = Integer.parseInt(panel.getAttribute("order"));

        panelData.setOrder(order);

        identifier = panel.getElementsByTagName("image").item(0).getFirstChild().getNodeValue();

        // Get the complet path of the file
        String ident = actual_chapter_number +
                "/" + actual_page_number + "/" +
                identifier; 
        panelData.setImageIdentifier(ident);

        // Obtains the type
        String type = panel.getAttribute("type");
        if(type.equals("withText"))
            panelData.setPanelType(PanelType.WithText);
        else
            panelData.setPanelType(PanelType.WithoutText);

        // Get the speech_bubbles
        NodeList speech_bubbles = panel.getElementsByTagName("speech_bubbles");
        initSpeech_bubbles(speech_bubbles, panelData);

        return panelData;
    }


    /**
     * Return the header from the file
     * @return HeaderData with all the data of the header
     */
    public HeaderData getHeader(){
        return header;
    }

    /**
     * Return the list of chapters
     * @return ArrayList of IChaptersData with all the chapters
     */
    public ArrayList<IChapterData> getChapters()
    {
        return chapters;
    }

    /***
     * Init the differents languages in the header
     * @param languages Element languages of the file
     */
    private void initHeaderLanguages(Element languages) {

        String defLang, auxLang, originalLang;
        String[] availables;

        defLang = languages.getAttribute("default");
        originalLang = languages.getAttribute("original");

        auxLang = languages.getAttribute("availables");
        availables = auxLang.split(";");

        // Set languages in the header
        for(int i = 0; i < availables.length; i++)
             this.header.addLanguage(availables[i]);

        // Set the default language
        this.header.setDefaultLanguage(defLang);

        // Set the original languange
        this.header.setOriginalLanguage(originalLang);
    }

    /**
     * Init the titles of the document
     * @param titleNodes List of titles
     */
    private void initHeaderTitles(NodeList titleNodes) {

        String titls;
        String langTitls;

        titls = "";
        langTitls = "";

        for(int i = 0; i < titleNodes.getLength(); i++)
        {
             titls = ((Element)titleNodes.item(i)).getFirstChild().getNodeValue();
             langTitls = ((Element)titleNodes.item(i)).getAttribute("language");

             //Add new title
             this.header.addTitle( titls, langTitls);
        }

    }
    /**
     * Init the author in the header data
     * @param authorNodes List of authors
     */
    private void initHeaderAuthors(NodeList authorNodes) throws XibException {

        ArrayList<String> namesAuthor;
        ArrayList<String> langAuthor;

        //For each author, get the name and the languages
        for(int i = 0; i < authorNodes.getLength(); i++)
        {
            NodeList names = ((Element)authorNodes.item(i)).getElementsByTagName("name");
            namesAuthor = new ArrayList();
            langAuthor = new ArrayList();


            //For each name, get the language
            for(int j = 0; j < names.getLength(); j++)
            {
                namesAuthor.add(
                        ((Element)names.item(j)).getFirstChild().getNodeValue());
                langAuthor.add(
                        ((Element)names.item(j)).getAttribute("language"));
            }

            //Set the author
            this.header.addAuthor( namesAuthor, langAuthor );

        }
    }

    /**
     * Init the page size in the header data
     * @param pageSize Element with page size
     */
    private void initHeaderPageSize(Element pageSize) {

        int width,height;
        width = Integer.parseInt(pageSize.getAttribute("width"));
        height = Integer.parseInt(pageSize.getAttribute("height"));

        this.header.setDefaultPageHeightAndWidth(width, height);
    }

    /**
     * Init the differents set of speechBubbles for each language
     * @param speech_bubbles NodeList with the list of speecBubbles
     * @param panelData PanelData to add the list of speecBubbles
     */
    private void initSpeech_bubbles(NodeList speech_bubbles, PanelData panelData) {

        int count = speech_bubbles.getLength();

        // For each additional language, add the speech_bubbles
        for(int i = 0; i < count; i++)
        {

            Element spch_buble = (Element) speech_bubbles.item(i);
            String language = spch_buble.getAttribute("language");

            ArrayList<ISpeechBubbleData> list = new ArrayList<ISpeechBubbleData>();



            NodeList listSpeechBubbles = spch_buble.getElementsByTagName("speech_bubble");

            for(int j = 0; j < listSpeechBubbles.getLength(); j++)
            {
                Element actualSpeechBubble = (Element) listSpeechBubbles.item(j);
                ISpeechBubbleData sbdata = new SpeechBubbleData();
                // TODO: Put catch expection here
                int positionX = Integer.parseInt(actualSpeechBubble.getAttribute("positionX"));
                int positionY = Integer.parseInt(actualSpeechBubble.getAttribute("positionY"));
                int height = Integer.parseInt(actualSpeechBubble.getAttribute("height"));
                int width = Integer.parseInt(actualSpeechBubble.getAttribute("width"));

                //Node nod = actualSpeechBubble.getFirstChild();
                String identifier = actualSpeechBubble.getElementsByTagName("image").item(0).getFirstChild().getNodeValue();
                String text = actualSpeechBubble.getElementsByTagName("text").item(0).getFirstChild().getNodeValue();

                if(identifier != null && !identifier.equals("")){
                   String ident = actual_chapter_number + "/" +
                           actual_page_number + "/" + language + "/" +
                           identifier;
                    sbdata.setImageIdentifier(ident);
                }

                sbdata.setLocationXY(positionX, positionY);
                sbdata.setWidthAndHeight(width, height);
                sbdata.setText(text);
                if(language.equals(header.getDefaultLanguage()))
                    sbdata.setSpeechBubbleType(ISpeechBubbleData.SpeechBubbleType.DefaultLanguage);
                else
                    sbdata.setSpeechBubbleType(ISpeechBubbleData.SpeechBubbleType.OtherLanguage);

                // Add to the list
                list.add(sbdata);

            }

            // Add the list to the panel
            panelData.setLanguage(language, list);

        }

    }
}
